Voxel Mosaics 2¶

Choose axial (A), coronal (C) or sagittal (S) slices. Modify with cross slices (X) and renderings (R).

See https://niivue.com/demos/features/mosaics2.html for mirror.

In [1]:
from pathlib import Path

from ipyniivue import download_dataset

BASE_API_URL = "https://niivue.com/demos/images/"
DATA_FOLDER = Path("images")

# Download data for example
download_dataset(
    BASE_API_URL,
    DATA_FOLDER,
    files=[
        "fslmean.nii.gz",
        "fslt.nii.gz",
    ],
)
fslmean.nii.gz already exists.
fslt.nii.gz already exists.
Dataset downloaded successfully to images.
In [2]:
import ipywidgets as widgets
from IPython.display import display

from ipyniivue import NiiVue

## Create NiiVue Instance and Load Data

nv = NiiVue(height=600, back_color=(1, 1, 1, 1), is_colorbar=True, drag_mode="CONTRAST")

nv.load_volumes(
    [
        {
            "path": DATA_FOLDER / "fslmean.nii.gz",
            "colormap": "gray",
            "colorbar_visible": False,
        },
        {
            "path": DATA_FOLDER / "fslt.nii.gz",
            "colormap": "warm",
            "colormap_negative": "winter",
            "cal_min": 1,
            "cal_max": 6,
        },
    ]
)

initial_mosaic = (
    "A 0 L+ 50 L- 60 C -10 0 S 40; A X 0 S X 0 C X 0 R A X 0 R S X 0 R C X 0"
)
nv.set_slice_mosaic_string(initial_mosaic)

## Create Interactive Controls

# mosaic string input
mosaic_text = widgets.Text(
    value=initial_mosaic,
    placeholder="Enter mosaic string",
    description="Mosaic string:",
    style={"description_width": "initial"},
    layout=widgets.Layout(width="70%"),
)

# help button
help_button = widgets.Button(
    description="Help",
    button_style="info",
    tooltip="Click for information about mosaic strings",
    layout=widgets.Layout(width="80px"),
)

# radiological convention checkbox
radio_check = widgets.Checkbox(value=False, description="Radiological", indent=False)

# world space checkbox
mm_check = widgets.Checkbox(value=False, description="World space", indent=False)

# ruler checkbox
ruler_check = widgets.Checkbox(value=False, description="Ruler", indent=False)

# nose left checkbox
sag_check = widgets.Checkbox(value=False, description="Nose left", indent=False)

# colorbar checkbox
colorbar_check = widgets.Checkbox(value=True, description="Colorbar", indent=False)

# negative colors checkbox
negative_check = widgets.Checkbox(
    value=True, description="Negative colors", indent=False
)

# orient cube checkbox
cube_check = widgets.Checkbox(value=False, description="Cube", indent=False)

# high DPI checkbox
dpi_check = widgets.Checkbox(value=True, description="HighDPI", indent=False)

# gamma slider
gamma_slider = widgets.FloatSlider(
    value=1.0, min=0.1, max=2.0, step=0.1, description="Gamma:", continuous_update=True
)

# drag mode dropdown
drag_mode = widgets.Dropdown(
    options=[
        ("none", 0),
        ("contrast", 1),
        ("measurement", 2),
    ],
    value=1,
    description="Drag mode:",
    style={"description_width": "initial"},
)

# output for messages
output = widgets.Output()

## Setup Event Handlers


def on_mosaic_change(change):
    """Handle mosaic string changes."""
    nv.set_slice_mosaic_string(change["new"])


def on_help_click(b):
    """Display help information."""
    with output:
        output.clear_output()
        print("Mosaic String Help:")
        print("=" * 50)
        print("Choose axial (A), coronal (C) or sagittal (S) slices.")
        print("Modify with cross slices (X) and renderings (R).")
        print("\nExample patterns:")
        print("  A 0 20 40        - Axial slices at positions 0, 20, 40")
        print("  C -10 0 10       - Coronal slices at positions -10, 0, 10")
        print("  S 0              - Sagittal slice at position 0")
        print("  A X 0            - Axial slice with cross at position 0")
        print("  R                - 3D rendering")
        print("  L+ 50            - Left hemisphere at position 50")
        print("  L- 60            - Right hemisphere at position 60")


def on_radio_change(change):
    """Handle radiological convention toggle."""
    nv.set_radiological_convention(change["new"])


def on_mm_change(change):
    """Handle world space toggle."""
    nv.set_slice_mm(change["new"])


def on_ruler_change(change):
    """Handle ruler toggle."""
    nv.opts.is_ruler = change["new"]


def on_sag_change(change):
    """Handle sagittal nose left toggle."""
    nv.opts.sagittal_nose_left = change["new"]


def on_colorbar_change(change):
    """Handle colorbar toggle."""
    nv.opts.is_colorbar = change["new"]


def on_negative_change(change):
    """Handle negative colormap toggle."""
    if change["new"]:
        nv.set_colormap_negative(nv.volumes[1].id, "winter")
    else:
        nv.set_colormap_negative(nv.volumes[1].id, "")


def on_cube_change(change):
    """Handle orient cube toggle."""
    nv.opts.is_orient_cube = change["new"]


def on_dpi_change(change):
    """Handle high DPI toggle."""
    nv.set_high_resolution_capable(change["new"])


def on_gamma_change(change):
    """Handle gamma adjustment."""
    nv.set_gamma(change["new"])


def on_drag_mode_change(change):
    """Handle drag mode change."""
    nv.opts.drag_mode = change["new"]


# attach event handlers
mosaic_text.observe(on_mosaic_change, names="value")
help_button.on_click(on_help_click)
radio_check.observe(on_radio_change, names="value")
mm_check.observe(on_mm_change, names="value")
ruler_check.observe(on_ruler_change, names="value")
sag_check.observe(on_sag_change, names="value")
colorbar_check.observe(on_colorbar_change, names="value")
negative_check.observe(on_negative_change, names="value")
cube_check.observe(on_cube_change, names="value")
dpi_check.observe(on_dpi_change, names="value")
gamma_slider.observe(on_gamma_change, names="value")
drag_mode.observe(on_drag_mode_change, names="value")

## Display All

# organize controls
mosaic_row = widgets.HBox([mosaic_text, help_button])

checkbox_row1 = widgets.HBox([radio_check, mm_check, ruler_check, sag_check])

checkbox_row2 = widgets.HBox([colorbar_check, negative_check, cube_check, dpi_check])

slider_row = widgets.HBox([gamma_slider, drag_mode])

# create main layout
controls = widgets.VBox([mosaic_row, checkbox_row1, checkbox_row2, slider_row, output])

# display everything
display(widgets.VBox([controls, nv]))